/* This file includes the especific code for Ruby bindings,
such as typemaps, memory management and macros */
%module "evolution"

%{
   #include <libecal/e-cal-component.h>
    typedef GSList* ECalComponentTextList;

	void check_error(GError* error, const char* msg_fmt) {
		 if (error) {
			char* msg = g_strdup(error->message);
			g_clear_error(&error);
			rb_raise(rb_eException, msg_fmt, msg);
		  }
	}
	/*Needed for some macros to work (specifically the DEFINE_EACH_FREE)*/
	typedef GSList Gslist;
	typedef GList Glist;
	typedef GObject* (*g_property_func) (GObject *);
	
	#define LANGUAGE_INIT_FUNCTION
	void lang_init_func(){
		rb_warning("Adding is_date to Time class!!");
		rb_define_attr(rb_cTime, "is_date",1,1);
	}
%}
/* This typemap is used to translate a function that returns a gboolean 
to an appropiate Ruby value*/

%typemap(ruby, out) gboolean {
    $result= $1?Qtrue: Qfalse;   
}

/* This typemap is used to translate a function that accepts a gboolean 
from an appropiate Ruby value*/

%typemap(ruby, in) gboolean {
    $1=$input==Qtrue?1: 0;   
}

%typemap(ruby, typecheck) gboolean {
	$1= ($input==Qtrue) || ($input==Qfalse) ?1: 0;   
}

/* A macro used to define generic outputs for GObjects. It returns
a previous Ruby instance for the object if there was one or creates
a new Ruby object that wraps the GOBject one and sets a pointer in the 
GObject to it for later retrieval.*/
%define RUBY_OBJ_FROM_GOBJ(class, obj, rb)
	rb = (VALUE)g_object_get_data(G_OBJECT(obj), "RUBY_PTR");
    if(!rb)
	{	   
       rb = SWIG_NewPointerObj((void *) obj, SWIGTYPE_p_ ## class , 1);
	   g_object_ref(obj);
	   g_object_set_data(G_OBJECT(obj), "RUBY_PTR", (gpointer)rb);	   
	}
%enddef


/* Macro defining output typemaps for GObjects*/
%define WRAP_OUTPUT(class)
    %typemap(out) class ## *{
        class *obj = $1;
        VALUE rb;
        if(obj)
        {
		RUBY_OBJ_FROM_GOBJ(class, obj, rb)
            	return rb;
        }
        else
        {
            return Qnil;
        }
    }

	%typemap(newfree) class ## *{
		if(!g_object_get_data(G_OBJECT($1), "RUBY_PTR"))
			g_object_set_data(G_OBJECT($1), "RUBY_PTR", (gpointer)self);	   
	  }
%enddef

%define DEFINE_LANGUAGE_DESTRUCTOR(class, funct)
   %extend class {
        ~ ## class () {         	
        	SWIG_RubyRemoveTracking(self);
			funct (self); 
					
        }
    }
%enddef

/* Wrap outputs for ESource &&  ESourceGroup */
WRAP_OUTPUT(ESource)
WRAP_OUTPUT(ESourceGroup)
WRAP_OUTPUT(ECalComponent)
WRAP_OUTPUT(ECal)

%typemap(out) char * {
    if ($1)
        $result = rb_str_new2($1);
    else
        $result = Qnil;
}

%typemap(out) g_char * {
    if ($1)
        $result = rb_str_new2($1);
    else
        $result = Qnil;
}

%typemap(out) must_free_g_char * {
    if ($1)
        $result = rb_str_new2($1);
    else
        $result = Qnil;
	g_free($1);
}
 
%typemap(out) ECalComponentDateTime {
    ECalComponentDateTime* etime=&$1;
    if(!etime->value)
    {
        $result=Qnil;
    }
    else
    {
        time_t val = icaltime_as_timet_with_zone(*etime->value,icaltimezone_get_builtin_timezone_from_tzid(etime->tzid));
		$result=rb_funcall( rb_cTime, rb_intern( "at" ), 1, INT2NUM(val));
		if(icaltime_is_date(*etime->value))
			rb_funcall( $result, rb_intern( "is_date=" ), 1, Qtrue);
		else
			rb_funcall( $result, rb_intern( "is_date=" ), 1, Qfalse);
        e_cal_component_free_datetime(etime);
    }
}

%typemap(in) ECalComponentDateTime {

	int is_date = RTEST(rb_funcall( $input, rb_intern( "is_date" ), 0));
	struct icaltimetype itt;
	if(!is_date)
	{
		int ticks = NUM2INT(rb_funcall($input, rb_intern("to_i"), 0));
		itt = icaltime_from_timet_with_zone(ticks, 0, icaltimezone_get_utc_timezone());
	}
	else
	{
		itt = icaltime_from_day_of_year(rb_funcall( $input, rb_intern( "yday" ), 0),
										rb_funcall( $input, rb_intern( "year" ), 0));
	}
    $1.value = &itt;
    $1.tzid = "UTC";
 }

%typemap(out) icaltimetype {
    time_t val = icaltime_as_timet($1);
    $result=rb_funcall( rb_cTime, rb_intern( "at" ), 1, INT2NUM(val));    
}

%typemap(out) icaltimetype* {
	if($1)
	{
		time_t val = icaltime_as_timet(*$1);
		$result=rb_funcall( rb_cTime, rb_intern( "at" ), 1, INT2NUM(val));
		e_cal_component_free_icaltimetype ($1);
	}
	else
		$result=Qnil;
}

%typemap(in) icaltimetype  {

    int ticks = NUM2INT(rb_funcall($input, rb_intern("to_i"), 0));
    struct icaltimetype itt = icaltime_from_timet_with_zone(ticks, 0, icaltimezone_get_utc_timezone());
    $1=itt;

 }

%typemap(in) icaltimetype*  {

    int ticks = NUM2INT(rb_funcall($input, rb_intern("to_i"), 0));
    struct icaltimetype itt = icaltime_from_timet_with_zone(ticks, 0, icaltimezone_get_utc_timezone());
    $1=&itt;

 }


%{
    static void obj_to_e_cal_component_text( VALUE obj, ECalComponentText* etxt)
    {
        VALUE tmp;
        if(TYPE(obj)==T_STRING)
        {
            etxt->value=RSTRING(obj)->ptr;
            etxt->altrep=NULL;
        }
        else if( (TYPE(obj)==T_ARRAY) && (RARRAY(obj)->len > 1) )
        {
            tmp=rb_ary_entry(obj,0);
            etxt->value= StringValuePtr(tmp);
            tmp=rb_ary_entry(obj,1);
            etxt->altrep= StringValuePtr(tmp);
        }
        else
          rb_raise(rb_eArgError, "wrong # of arguments must be a string or array with at least 2 elements");    
    }
%}

%typemap(in) ECalComponentText  {
    obj_to_e_cal_component_text($input, &$1);
}

%{
  VALUE e_cal_component_text_to_obj(ECalComponentText* t)
  {
    VALUE txt1=Qnil,txt2=Qnil;
    if(t->value)
        txt1 = rb_str_new2(t->value);
    if(t->altrep)
        txt2 = rb_str_new2(t->altrep);
    if((txt2==Qnil)&&(txt1!=Qnil))
        return txt1;
    else if((txt2!=Qnil)&&(txt1==Qnil))
        return txt2;
    else if((txt2!=Qnil)&&(txt1!=Qnil))
        return rb_ary_new3(2, txt1, txt2);
    else
       return Qnil;

  }
%}
%typemap(out) ECalComponentText  {
   $result = e_cal_component_text_to_obj(&$1);    
}



%{
   static void  add_text_element(gpointer data, gpointer user_data)
   {
     rb_ary_push((VALUE)user_data, e_cal_component_text_to_obj((ECalComponentText*) data));
   }
%}

%typemap(out) ECalComponentTextList  {
    VALUE rb=rb_ary_new();
    g_slist_foreach($1,add_text_element, (gpointer)rb);
    $result=rb;
    e_cal_component_free_text_list($1);
}



%typemap(in) ECalComponentTextList  {
    ECalComponentTextList l=NULL;
    ECalComponentText* t;
    int i;
    if (TYPE($input) != T_ARRAY)
         rb_raise(rb_eArgError, "Argument passed must be an array");
    for(i=0; i< RARRAY($input)->len; i++)
    {
        t=g_new(ECalComponentText,1);
        obj_to_e_cal_component_text(rb_ary_entry($input,i), t);
        l=g_slist_append(l, t);
    }
    $1=l;
}

/* Convert an ESourceList in a Rubylist of ESourceGroups */

%typemap(out) ESourceList* {
  GSList *p;
  ESourceList *list = $1;
  VALUE egrp;
  $result=rb_ary_new();
  for (p = e_source_list_peek_groups(list); p != NULL; p = p->next) {	 
        ESourceGroup *group = E_SOURCE_GROUP (p->data);
        RUBY_OBJ_FROM_GOBJ(ESourceGroup, group, egrp)
        rb_ary_push($result,egrp );			         			        
  }
  g_object_unref(list);
}


/* ITERATE over a GLIB collection and yield the objects */
%define DEFINE_EACH_PEEK(class, iterator_class, collection_class, func_collection)

	%extend class {
	%{
		static void yield_element_ ## iterator_class(iterator_class* obj,gpointer user_data)	{
			VALUE rb;
			RUBY_OBJ_FROM_GOBJ(iterator_class, obj, rb)
			rb_yield(rb);
		}
	%}
    };
	ITERATE_OVER_COLLECTION(class, iterator_class, collection_class, func_collection, yield_element_ ## iterator_class)
	%extend class {
    	class* each () {	     
			g_## collection_class ## _foreach(func_collection(self), ## class ## _iterate_ ## iterator_class ## _with_ ## func_collection ##_apply_yield_element_ ## iterator_class, NULL);			
			return self;
		}

	};
%enddef


/* ITERATE over a GLIB collection and yield the objects. Finally free the slist */
%define DEFINE_EACH_FREE(class, iterator_class, collection_class, func_collection)

	%extend class {
	%{
		static void yield_element_ ## iterator_class(iterator_class* obj,gpointer user_data)	{
			VALUE rb;
			RUBY_OBJ_FROM_GOBJ(iterator_class, obj, rb)
			rb_yield(rb);
		}
	%}
    };
	ITERATE_OVER_COLLECTION(class, iterator_class, collection_class, func_collection, yield_element_ ## iterator_class)
	%extend class {
	class* each () {	     
			G##collection_class* l = func_collection(self);
			g_## collection_class ## _foreach(l, ## class ## _iterate_ ## iterator_class ## _with_ ## func_collection ##_apply_yield_element_ ## iterator_class, NULL);			
			g_## collection_class ## _free(l);
			return self;
		}

	};
%enddef 


/* ITERATE over a GLIB collection and mark the ruby objects if they exist */
%define DEFINE_MARK_ITERATION_PEEK(class, iterator_class, collection_class, func_collection)
	%extend class {

	%{
		static void mark_element_##iterator_class(iterator_class* obj, gpointer user_data)	{
			VALUE rb;
			rb = (VALUE)g_object_get_data(G_OBJECT(obj), "RUBY_PTR");
			if(rb)
				rb_gc_mark(rb);
		}
    %}
    };
	ITERATE_OVER_COLLECTION(class, iterator_class, collection_class, func_collection, mark_element_##iterator_class)
    %{	
	static void class ## _markfunc (void* ptr) {	     	
			g_## collection_class ## _foreach(func_collection((class *)ptr), ## class ## _iterate_ ## iterator_class ## _with_ ## func_collection ##_apply_mark_element_##iterator_class, NULL);						
		}
	%}

%enddef

/* ITERATE over a GLIB collection and mark the ruby objects if they exist */
%define DEFINE_MARK_ITERATION_FREE(class, iterator_class, collection_class, func_collection)
    %extend class {
	%{
		static void mark_element_##iterator_class(iterator_class* obj, gpointer user_data)	{
			VALUE rb;
			rb = (VALUE)g_object_get_data(G_OBJECT(obj), "RUBY_PTR");
			if(rb)
				rb_gc_mark(rb);
		}
    %}	
    }
	ITERATE_OVER_COLLECTION(class, iterator_class, collection_class, func_collection, mark_element_##iterator_class)
    %{
	static void class ## _markfunc (void* ptr) {	     	
			G##collection_class* l = func_collection((class *)ptr);
			g_## collection_class ## _foreach(l, ## class ## _iterate_ ## iterator_class ## _with_ ## func_collection ##_apply_mark_element_##iterator_class, NULL);						
			g_## collection_class ## _free(l);			
		}
	%}

%enddef

/* Get a property and MARK it */
%define DEFINE_MARK_PROPERTY(class, get_property)
%{
	static void class ## _markfunc (void* ptr) {	     	
		class* src = (class*)ptr;
		GObject* prop = get_property((class *)ptr);
		VALUE rb= (VALUE) g_object_get_data(prop, "RUBY_PTR");
		if(rb)
			rb_gc_mark(rb);
	}
%}
%enddef

/* Get a list of properties and MARK them */
%define DEFINE_MARK_PROPERTIES(class, property_array)
%{
	
	static void class ## _markfunc (void* ptr) {	     	
		g_property_func f_array[] = property_array;
		int ii , n = sizeof(f_array)/sizeof(g_property_func);
		for(ii=0; ii < n; ii ++) {
			GObject* prop = f_array[ii]((GObject*)ptr);
			VALUE rb= (VALUE) g_object_get_data(prop, "RUBY_PTR");
			if(rb)
				rb_gc_mark(rb);
		}
	}
%}
%enddef

%mixin ESourceGroup "Enumerable";
%mixin ECal "Enumerable";

%markfunc ESourceGroup "ESourceGroup_markfunc";
%markfunc ESource "ESource_markfunc";
%markfunc ECal "ECal_markfunc";

%alias ESourceGroup::append "<<";
%alias ESourceGroup::append "add";
%alias ECal::append "<<";
%alias ECal::append "add";
%alias ECal::del "remove";
%alias ECal::del "delete";

%include "evolution.i"

DEFINE_EACH_PEEK(ESourceGroup, ESource, slist, e_source_group_peek_sources)
DEFINE_EACH_FREE(ECal, ECalComponent, list, ECal_get_list)
DEFINE_MARK_ITERATION_PEEK(ESourceGroup, ESource, slist, e_source_group_peek_sources)
/*CHECK: Does this really make sense? Isn't ECalComponent recreated *every* time? */
DEFINE_MARK_ITERATION_FREE(ECal, ECalComponent, list, ECal_get_list)
DEFINE_MARK_PROPERTIES(ESource, {(g_property_func)e_source_peek_group})






